// by Teruaki Gemma 17/May/2008

//Reference
//Thomas M. J. Fruchterman, Edward M. Reingold:
//Graph Drawing by Force-directed Placement (1991) 
//http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol21/issue11/spe060tf.pdf

//Cowboy Programming » Blob Physics: By Mick West
//http://cowboyprogramming.com/2007/01/05/blob-physics/

//連想検索エンジンreflexa（リフレクサ）はコトバとコトバのつながりを元に未知の情報を探索していくための連想検索エンジンです。
//http://labs.preferred.jp/reflexa/



//choose K experimentally.
var K = 75;

var graph = [];
var vertices = [];
var vertices_prev = [];
var edges = [];
var comb = [];

var ctx = null;
var ctx_size = 640;

function add_vector(a,b) {return [a[0] + b[0], a[1] + b[1]]};
function add_vector3(a,b,c) {return [a[0] + b[0] + c[0], a[1] + b[1] + c[1]]};
function sub_vector(a,b) {return [a[0] - b[0], a[1] - b[1]]};
function scale_vector(k,v) {return [k * v[0], k * v[1]]};
function length_vector(v) {return Math.sqrt(Math.pow(v[0],2) + Math.pow(v[1],2))};

function init(x) {
  //Adjacency List Representation
  switch (x) {
  case 0:
    graph =[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],
	    [0,2,18,19],
	    [1,20],
	    [5],
	    [10],
	    [0,3,8,9],
	    [10],
	    [10],
	    [5],
	    [5],
	    [0,4,6,7],
	    [12,15,20],
	    [11,15,20],
	    [0,14],
	    [13],
	    [11,12,20],
	    [0],
	    [0],
	    [1],
	    [1],
	    [0,2,11,12,15]];

    vertices = graph.map(function(x,i) {return [ctx_size * Math.random(),ctx_size * Math.random()]});
    break;
  }
  vertices_prev = vertices.map(Prototype.K);
  graph.each(function(x,i) {
      x.each(function(y) {
	  if (i > y && graph[y].include(i))
	    edges.push([y,i])})});
  for (var i = 0; i < graph.length; i++)
    for (var j = i + 1; j < graph.length; j++)
      comb.push([i,j]);
}

function process_placement() {
  var force = vertices.map(function(x) {return []});
  comb.each(function(e) {
      var v = sub_vector(vertices[e[0]],vertices[e[1]]);
      var d = length_vector(v);
      if (d != 0) {
	var force_repulsive = Math.pow(K,2) / d;
	force[e[0]].push(scale_vector(force_repulsive / d, v));
	force[e[1]].push(scale_vector(-force_repulsive / d, v));
      }
    });
  edges.each(function(e) {
      var v = sub_vector(vertices[e[0]],vertices[e[1]]);
      var d = length_vector(v);
      if (d != 0) {
	var force_attractive = Math.pow(d,2) / K;
	force[e[0]].push(scale_vector(-force_attractive / d, v));
	force[e[1]].push(scale_vector(force_attractive / d, v));

      }
    });
  //Sums each forces.
  force = force.map(function(x) {return x.inject([0,0],add_vector)});
  //Verlet Integration
  var vertices_t = vertices.map(Prototype.K);
  vertices = vertices.map(function(x,i) {
      return constraint_move(add_vector3(x,
					 sub_vector(x,vertices_prev[i]),
					 scale_vector(0.1, force[i])),
			     x)});
  vertices_prev = vertices_t;
}

function constraint_move(pos,prev) {
  var r = 5;
  return [Math.max(Math.min(pos[0], prev[0]+r, ctx_size - 80), prev[0]-r, 80),
	  Math.max(Math.min(pos[1], prev[1]+r, ctx_size - 18), prev[1]-r, 18)];
};

function draw_graph() {
  //draw edges.
  ctx.beginPath();
  edges.each(function(e) {
      ctx.moveTo(vertices[e[0]][0],vertices[e[0]][1]);
      ctx.lineTo(vertices[e[1]][0],vertices[e[1]][1]);
    });
  ctx.stroke();
  ctx.drawImage($('gemma'), vertices[18][0],vertices[18][1]);
  //draw vertices.
  vertices.each(function(x,i) {
      ctx.drawImage($('letter' + i), x[0] - 80,x[1] - 18);
    });
}

function main() {
  var canvas = document.getElementById("canvas");
  if (!canvas.getContext)
    $('msg').innerHTML = 'unsupported';
  ctx = canvas.getContext('2d');
  init(0);
  draw();
}

function draw() {
  ctx.clearRect(0,0,ctx_size,ctx_size);
  draw_graph();
  process_placement();
}

var timer = null;
function start_timer() {
  if (timer) clearInterval(timer);
  timer = setInterval(draw,100);
}
function stop_timer() {
  clearInterval(timer);
}

window.onload = main;

